#!/usr/bin/env python
#
# Copyright 2013 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Dashboard for network provider to see results from their probe."""
__author__ = "Joey Freeland (joeyfreeland@gmail.com)"


import datetime
import json
import numpy
import os

import webapp2

import models
import tools

from google.appengine.api import users
from google.appengine.ext import db
from google.appengine.ext.webapp import template


def FetchDailyResults(provider):
  """Fetch daily results for a provider."""

  # TODO(joeyfreeland): Need to add the ability to pass in a timezone so that we
  # can select the correct timezone based on the location of the device.
  now = datetime.datetime.utcnow()
  one_day_ago = now - datetime.timedelta(days=1)
  #peak_end = datetime.datetime.combine(one_day_ago.date(),
  #                                     datetime.time(15, 00))
  #peak_start = datetime.datetime.combine(now.date(), datetime.time(0, 00))
  if provider != "All" and provider != "":
    #current_day = (db.Query(models.TestResult).filter("provider = ", provider)
    #               .filter("date >= ", peak_start).order("-date"))
    current_day = (db.Query(models.TestResult).filter("provider = ", provider)
                   .filter("date >= ", one_day_ago).order("-date"))
    #previous_day = (db.Query(models.TestResult).filter("provider = ", provider)
    #                .filter("date <= ", peak_end)
    #                .filter("date >= ", one_day_ago).order("-date"))
  else:
    #current_day = (db.Query(models.TestResult).filter("date >= ", peak_start)
    #               .order("-date"))
    current_day = (db.Query(models.TestResult).filter("date >= ", one_day_ago)
                   .order("-date"))
    #previous_day = (db.Query(models.TestResult).filter("date >= ", one_day_ago)
    #                .filter("date <= ", peak_end).order("-date"))
  results_list = []
  for result in current_day:
    results_list.append(result)
  #for result in previous_day:
  #  results_list.append(result)
  return results_list


def FetchLatestResultDate(provider):
  if provider != "All" and provider != "":
    pass


def FormatBPSOutput(bps):
  ONE_KBIT = 1024
  ONE_MBIT = 1048576
  if bps > ONE_KBIT and bps < ONE_MBIT:
    bps = bps / 1024.0
    return "%.1f Kbps" % bps
  if bps > ONE_MBIT:
    bps = bps / 1048576.0
    return "%.1f Mbps" % bps
  else:
    return "Problem"


class GenerateCSV(webapp2.RequestHandler):
  """Generate an XML file representing a provider's locations and ssids."""

  def get(self, provider, location):
    user = users.get_current_user()
    authorized_user = tools.ValidateUser(user)
    if authorized_user:
      results = (db.Query(models.TestResult).filter("provider = ", provider)
                 .filter("location = ", location))
      result_list = []
      #output = "\"Provider\",\"Location\",\"NDT Download\"\n"
      for result in results:
        result_list.append(result)
      #  output += "\"%s\",\"%s\",\"%s\"\n" % (result.provider, result.location,
      #                                        result.ndt_down)
      template_values = {"results": result_list}
      path = os.path.join(os.path.dirname(__file__), "templates/csv.csv")
      self.response.headers["Content-Type"] = "text/csv"
      self.response.out.write(template.render(path,
                                              template_values).decode("utf-8"))
      #self.response.out.write(output)


def GenerateMetricStatistics(results):
  pass


def GenerateDashboard(provider, metrics):
  pass


def GenerateHistoryDashboardData(provider):
  """Generate data to be displayed in the history dashboard."""

  if provider != "All" and provider != "":
    devices = (db.Query(models.WiFiTestingDevice).filter("provider = ",
                                                         provider))
  else:
    devices = db.Query(models.WiFiTestingDevice) 
  results = {}
  for device in devices:
    if device.provider not in results.keys():
      results[device.provider] = {}
    if device.device_name not in results[device.provider].keys():
      results[device.provider][device.device_name] = 0
  return results


def GenerateSimpleDashboardData(provider):
  """Generate data to be displayed in the simple dashboard."""

  results, template_data = {}, {}
  results_list = FetchDailyResults(provider)
  tracked_metrics = {"local_st_average_download": "local_speedtest_down",
                     "remote_st_average_download": "remote_speedtest_down",
                     "ndt_average_download": "ndt_down"}
  for result in results_list:
    location = result.location
    ssid = result.ssid
    if location not in results.keys():
      results[location], template_data[location] = {}, {}
    if ssid not in results[location].keys():
      results[location][ssid] = {}
      results[location][ssid]["median"] = {}
      results[location][ssid]["stdev"] = {}
      template_data[location][ssid] = {}
      template_data[location][ssid]["location"] = location
      template_data[location][ssid]["provider"] = result.provider
      template_data[location][ssid]["ssid"] = ssid
    if "last_update_text" not in template_data[location][ssid].keys():
      (template_data[location][ssid]["last_update_text"],
       template_data[location][ssid]["last_update_color"]) =\
       GenerateTimeDeltaString(result.date)
    for k, v in tracked_metrics.iteritems():
      if k not in results[location][ssid]:
        text = "%s_text" % k
        results[location][ssid][k] = []
        results[location][ssid]["median"][k] = 0
        results[location][ssid][text] = "None"
      try:
        value = result.__getattribute__(v)
        if value:
          results[location][ssid][k].append(int(result.__getattribute__(v)))
      except:
        pass
  # TODO(joeyfreeland): Loop through the dictionaries I've just created to get
  # some basic statistics in order to do future comparisons.
  for k, v in tracked_metrics.iteritems():
    for location, ssids in results.iteritems():
      for ssid in ssids.iterkeys():
        while int(0) in results[location][ssid][k]:
          results[location][ssid][k].remove(int(0))
        results_array = numpy.array(results[location][ssid][k])
        results[location][ssid]["median"][k] = numpy.median(results_array)
        results[location][ssid]["stdev"][k] = numpy.std(results_array)
  for location, ssids in results.iteritems():
    removed = False
    for ssid, data in ssids.iteritems():
      if removed is True:
        continue
      valid = False
      for metric, result in data.iteritems():
        if metric in tracked_metrics and len(result) > 2:
          valid = True
          continue
      print template_data
      if not valid:
        del template_data[location]
        removed = True
        continue
      for metric, result in data.iteritems():
          if metric in tracked_metrics.keys():
            median = results[location][ssid]["median"][metric]
            stdev = results[location][ssid]["stdev"][metric]
            values = []
          else:
            continue
          for i in result:
            if i and i < (median + stdev) and i > (median - stdev):
              try:
                values.append(i)
              except:
                pass
          if values:
            mean = int(numpy.mean(numpy.array(values)))
            text = "%s_text" % metric
            template_data[location][ssid][metric] = mean
            template_data[location][ssid][text] = FormatBPSOutput(mean)
          else:
            template_data[location][ssid][metric] = 0
  return template_data


def GenerateTimeDeltaString(result_date):
  ONE_HOUR = 3600
  time_delta = datetime.datetime.utcnow() - result_date
  if time_delta.seconds >= ONE_HOUR:
    hours = time_delta.seconds / ONE_HOUR
    minutes = (((time_delta.seconds % ONE_HOUR) / 60) / 60.0 ) * 10 
    text = "%d.%d hours ago" % (hours, minutes)
    return text, "red"
  else:
    minutes = time_delta.seconds / 60
    seconds = int(((time_delta.seconds % 60) / 60.0 * 10))
    text = "%d.%d minutes ago" % (minutes, seconds)
    if minutes > 30:
      return text, "red"
    else:
      return text, None


class GenerateProviderXML(webapp2.RequestHandler):
  """Generate an XML file representing a provider's locations and ssids."""

  def post(self):
    user = users.get_current_user()
    authorized_user = tools.ValidateUser(user)
    if authorized_user:
      location_dict = {}
      r = self.request
      provider = r.get("provider")
      locations = db.GqlQuery("SELECT DISTINCT location FROM TestResult WHERE "
                              "provider = :1", provider)
      for location_result in locations:
        ssid_list = []
        ssids = db.GqlQuery("SELECT DISTINCT ssid FROM TestResult WHERE "
                            "provider = :1 and location = :2", provider,
                            location_result.location)
        for ssid_result in ssids:
          ssid_list.append(ssid_result.ssid)
        location_dict[location_result.location] = ssid_list
      template_values = {"locations": location_dict,
                         "provider": provider,
                         "user": authorized_user.email}
      path = os.path.join(os.path.dirname(__file__), "templates/details.xml")
      self.response.headers["Content-Type"] = "text/xml"
      self.response.out.write(template.render(path,
                                              template_values).decode("utf-8"))


class GenerateSSIDData(webapp2.RequestHandler):
  """Generate text that represents the data tables we want to load."""

  def post(self):
    user = users.get_current_user()
    authorized_user = tools.ValidateUser(user)
    if authorized_user:
      r = self.request
      provider = r.get("provider")
      location = r.get("location")
      ssid = r.get("ssid")
      two_days_ago = datetime.datetime.utcnow() - datetime.timedelta(days=2)
      results = (db.Query(models.TestResult).filter("provider = ", provider)
                 .filter("location = ", location).filter("ssid = ", ssid)
                 .filter("date >= ", two_days_ago).order("-date")).run()
      if results:
        self.response.headers["Content-Type"] = "application/json"
        self.response.out.write(json.dumps([r.to_dict() for r in results]))
      else:
        self.response.out.write("Query returned no results.")


class ViewDeviceSSIDDashboard(webapp2.RequestHandler):
  """Display the results for a specific provider."""

  def get(self, provider, device, ssid):
    user = users.get_current_user()
    authorized_user = tools.ValidateUser(user)
    if authorized_user:
      if (provider == authorized_user.provider) or (authorized_user.provider ==
                                                    "All"):
        # results = (db.Query(models.TestResult).filter("provider = ", provider)
        #           .filter("location = ", device).filter("ssid = ", ssid)
        #           .order("-date"))
        # output = json.dumps([r.to_dict() for r in results.run()])
        template_values = {"provider": provider,
                           "device": device,
                           "ssid": ssid,
                           # "results": output,
                          }
        path = os.path.join(os.path.dirname(__file__),
                            "templates/provider_device_ssid.html")
        self.response.out.write(template.render(path, template_values)
                                .decode("utf-8"))
      else:
        self.response.out.write("You are not authorized to view this page.")
    else:
      self.response.out.write("This is not a valid user.")


class ViewProvider(webapp2.RequestHandler):
  """Display the results for a specific provider."""

  def get(self, provider):
    user = users.get_current_user()
    authorized_user = tools.ValidateUser(user)
    if authorized_user:
      if (provider == authorized_user.provider) or (authorized_user.provider ==
                                                    "All"):
        if provider == "all":
          self.response.out.write("This user has access to view everything.\n")
        else:
          template_values = {"provider": provider}
          path = os.path.join(os.path.dirname(__file__),
                              "templates/provider.html")
          self.response.out.write(template.render(path, template_values)
                                  .decode("utf-8"))


class ViewHistoryDashboard(webapp2.RequestHandler):
  """Display a provider's list of SSID's and a link to download all data."""

  def get(self, provider):
    user = users.get_current_user()
    authorized_user = tools.ValidateUser(user)
    if authorized_user:
      if (provider == authorized_user.provider) or (authorized_user.provider ==
                                                    "All"):
        dashboard_data = GenerateHistoryDashboardData(provider)
        template_values = {"provider": provider,
                           "results": dashboard_data,
                           "date": datetime.datetime.utcnow()}
        path = os.path.join(os.path.dirname(__file__),
                            "templates/historydashboard.html")
        self.response.out.write(template.render(path, template_values)
                                .decode("utf-8"))
      else:
        self.response.out.write("You are not authorized to view this page.")
    else:
      self.response.out.write("This is not a valid user.")


class ViewSimpleDashboard(webapp2.RequestHandler):
  """Display the results for a specific provider."""

  def get(self, provider):
    user = users.get_current_user()
    authorized_user = tools.ValidateUser(user)
    if authorized_user:
      if (provider == authorized_user.provider) or (authorized_user.provider ==
                                                    "All"):
        dashboard_data = GenerateSimpleDashboardData(provider)
        template_values = {"provider": provider,
                           "results": dashboard_data,
                           "date": datetime.datetime.utcnow()}
        path = os.path.join(os.path.dirname(__file__),
                            "templates/simpledashboard.html")
        self.response.out.write(template.render(path, template_values)
                                .decode("utf-8"))
      else:
        self.response.out.write("You are not authorized to view this page.")
    else:
      self.response.out.write("This is not a valid user.")


app = webapp2.WSGIApplication([(r"/csv/(.*)/(.*).csv", GenerateCSV),
                               (r"/dashboard/(.*)", ViewSimpleDashboard),
                               (r"/device/(.*)/(.*)/(.*)",
                                ViewDeviceSSIDDashboard),
                               (r"/history/(.*)", ViewHistoryDashboard),
                               ("/provider/ssiddata", GenerateSSIDData),
                               ("/provider/details.xml", GenerateProviderXML),
                               (r"/provider/(.*)", ViewProvider),
                              ],
                              debug=True)


def main():
  app.run()


if __name__ == "__main__":
  main()
